---
id: signalr
title: 24. 即时通讯
sidebar_label: 24. 即时通讯
---

## 24.1 什么是即时通讯

即时通讯（Instant messaging，简称 IM）通常是指互联网上用以进行实时通讯的系统，允许两人或多人使用网络即时的传递文字信息、文档、语音与视频交流。

即时通讯不同于 e-mail 在于它的交谈是实时的。大部分的即时通讯服务提供了状态信息的特性 ── 显示联络人名单，联络人是否在线上与能否与联络人交谈。

在互联网上目前使用较广的即时通讯服务包括 Windows Live Messenger、AOL Instant Messenger、skype、Yahoo! Messenger、NET Messenger Service、Jabber、ICQ 与 QQ 等。

## 24.2 即时通讯应用场景

即时通讯应用场景非常广泛，需要实时交互消息的都需要。如：

- 聊天工具：QQ、WeChat、在线客服等
- 手游网游：王者荣耀、魔兽等
- 网络直播：腾讯课堂、抖音直播等
- 订单推送：美团、餐饮下单系统等
- 协同办公：公司内部文件分享、工作安排、在线会议等。

以上只是列举了比较常用的应用场景，但即时通讯的作用远不止于此。

文档紧急编写中，可以先看官方文档：https://docs.microsoft.com/zh-cn/aspnet/core/signalr/introduction?view=aspnetcore-5.0

## 24.3 关于 `SignalR`

即时通讯技术实现是复杂且过于底层化，所以微软为了简化即时通讯应用程序，开发出了一个强大且简易使用的通信库：`SignalR`，通过该库我们可以轻松实现类似 QQ、微信这类 IM 聊天工具，也能快速实现消息推送、订单推送这样的系统。

### 24.3.1 微软官方介绍

ASP.NET Core SignalR 是一种开放源代码库，可简化将实时 web 功能添加到应用程序的功能。 实时 web 功能使服务器端代码可以立即将内容推送到客户端。

适用于 SignalR ：

- 需要从服务器进行高频率更新的应用。 示例包括游戏、社交网络、投票、拍卖、地图和 GPS 应用。
- 仪表板和监视应用。 示例包括公司仪表板、即时销售更新或旅行警报。
- 协作应用。 协作应用的示例包括白板应用和团队会议软件。
- 需要通知的应用。 社交网络、电子邮件、聊天、游戏、旅行警报和很多其他应用都需使用通知。

目前 `SignalR` 已经内置在 `.NET 5 SDK` 中。同时 `SignalR` 支持 `Web、App、Console、Desktop` 等多个应用平台。

## 24.4 注册 `SignalR` 服务

在 `Furion` 框架中，任何服务功能都需要先注册后再使用，`SignalR` 也不例外。只需要在 `Startup.cs` 中添加注册即可：

```cs {1,15,22,25}
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace Furion.Web.Core
{
    public sealed class Startup : AppStartup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            // 其他代码...

            // 添加即时通讯
            services.AddSignalR();
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            // 其他代码...

            app.UseEndpoints(endpoints =>
            {
                // 注册集线器
                endpoints.MapHubs();

                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}
```

## 24.5 `SignalR` 长连接和集线器

`SignalR` 包含两种用于在客户端和服务器之间进行通信的模型：`持久性连接`和 `集线器` 中心。

### 25.5.1 持久性连接

连接表示用于发送单接收方、分组或广播消息的简单终结点。 `持久性连接` (在 .NET 代码中由 PersistentConnection 类表示) 使开发人员能够直接访问 `SignalR` 公开的低级别通信协议。 使用基于连接的 Api （如 Windows Communication Foundation）的开发人员将对使用连接通信模型非常熟悉。

### 24.5.2 集线器

集线器是一种基于连接 API 构建的更高级别管道，**它允许客户端和服务器直接调用方法**。 `SignalR` 就像魔术一样处理跨机器边界的调度，使客户端能够像本地方法一样轻松地调用服务器上的方法，反之亦然。 如果开发人员已使用远程调用 （如 .NET 远程处理），则将对使用中心通信模型非常熟悉。 使用集线器还可以将强类型参数传递给方法，从而启用模型绑定。

:::note 小知识

想了解更多关于 `持久性连接` 和 `集线器中心` 可查阅 [SignalR 官方文档](https://docs.microsoft.com/zh-cn/aspnet/signalr/overview/getting-started/introduction-to-signalr#connections-and-hubs)

:::

## 24.6 集线器 `Hub` 定义

**在本章节中主要推荐使用集线器通信模型方式。**这里主要说明 `Hub` 定义，如果无法理解该通信模型的作用也没关系，接下来的例子会带大家慢慢熟悉并使用。

### 24.6.1 两种定义方式

定义集线器只需要继承 `Hub` 或 `Hub<TStrongType>` 泛型基类即可，如：

- `Hub` 方式

```cs {9}
using Furion.InstantMessaging;
using Microsoft.AspNetCore.SignalR;

namespace Furion.Core
{
    /// <summary>
    /// 聊天集线器
    /// </summary>
    public class ChatHub : Hub
    {
        // 定义一个方法供客户端调用
        public Task SendMessage(string user, string message)
        {
            // 触发客户端定义监听的方法
            return Clients.All.SendAsync("ReceiveMessage", user, message);
        }
    }
}
```

- `Hub<TStrongType>` 类型方式

```cs
public interface IChatClient
{
    Task ReceiveMessage(string user, string message);
}
```

```cs {1}
public class StronglyTypedChatHub : Hub<IChatClient>
{
    // 定义一个方法供客户端调用
    public async Task SendMessage(string user, string message)
    {
        // 触发客户端定义监听的方法
        await Clients.All.ReceiveMessage(user, message);
    }
}
```

通过使用 `Hub<IChatClient>` 可以对客户端方法进行编译时检查。 这可以防止由于使用神奇字符串而导致的问题，因为 `Hub<T>` 只能提供对在接口中定义的方法的访问。

### 24.6.2 `[MapHub]` 配置连接地址

在 `SignalR` 库中要求每一个公开的集线器都需要配置客户端连接地址，所以，`Furion` 框架提供了更加 `[MapHub]` 配置，如：

```cs {1,11}
using Furion.InstantMessaging;
using Microsoft.AspNetCore.SignalR;
using System;
using System.Threading.Tasks;

namespace Furion.Core
{
    /// <summary>
    /// 聊天集线器
    /// </summary>
    [MapHub("/hubs/chathub")]
    public class ChatHub : Hub
    {
        // ...
    }
}
```

:::important `SignalR` 原生配置方式

在 `Furion` 中推荐使用 `[MapHub]` 方式配置集线器客户端连接地址，当然也可以使用 `SignalR` 提供的方式，如在 `Startup.cs` 配置：

```cs {10}
public sealed class Startup : AppStartup
{
   // 其他代码
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        // 其他代码...
        app.UseEndpoints(endpoints =>
        {
            // 注册集线器
            endpoints.MapHub<ChatHub>("/hubs/chathub");
        });
    }
}
```

:::

### 24.6.3 `Hub` 注册更多配置

有些时候，我们需要注册 `Hub` 时配置更多参数，比如权限、跨域等，这时只需要在 `Hub` 派生类中编写以下静态方法即可：

```cs {1,8,13,18}
using Furion.InstantMessaging;
using Microsoft.AspNetCore.SignalR;
using System;
using System.Threading.Tasks;

namespace Furion.Core
{
    [MapHub("/hubs/chathub")]
    public class ChatHub : Hub
    {
        // 其他代码

        public static void HttpConnectionDispatcherOptionsSettings(HttpConnectionDispatcherOptions options)
        {
            // 配置
        }

        public static void HubEndpointConventionBuilderSettings(HubEndpointConventionBuilder Builder)
        {
            // 配置
        }
    }
}
```

以上配置等价于 `SignalR` 在 `Startup.cs` 中的配置：

```cs
app.UseEndpoints(endpoints =>
{
    var builder = endpoints.MapHub<ChatHub>("/hubs/chathub", options =>
       {
           // 配置
       });
});
```

## 24.7 服务端和客户端双工通信

### 24.7.1 触发所有客户端代码

```cs
Clients.All.客户端方法(参数);
```

### 24.7.2 触发调用者客户端

```cs
Clients.Caller.客户端方法(参数);
```

### 24.7.3 触发除了调用者以外的客户端

```cs
Clients.Others.客户端方法(参数);
```

### 24.7.4 触发特定用户客户端

```cs
Clients.User("用户").客户端方法(参数);
```

### 24.7.5 触发多个用户客户端

```cs
Clients.Users("用户","用户2",...).客户端方法(参数);
```

### 24.7.6 触发分组内客户端

```cs
Clients.Group("分组").客户端方法(参数);
```

### 24.7.7 触发多个分组客户端

```cs
Clients.Groups("分组","分组2",...).客户端方法(参数);
```

### 24.7.8 触发分组外的客户端

```cs
Clients.GroupExcept("分组").客户端方法(参数);
```

## 24.8 自定义用户唯一标识

## 24.9 分组管理

## 24.10 各个客户端连接 API

### 24.10.1 `Javascript` 客户端

### 24.10.2 `Typescript` 客户端

### 24.10.3 `.NET` 客户端

### 24.10.4 `Java` 客户端

## 24.11 常见例子

### 24.11.1 实现消息广播、推送

整理中...

### 24.11.2 实现聊天功能

整理中...

### 24.11.3 实现 `你画我来猜`

整理中...

## 24.12 反馈与建议

:::note 与我们交流

给 Furion 提 [Issue](https://gitee.com/dotnetchina/Furion/issues/new?issue)。

:::

---

:::note 了解更多

想了解更多 `SignalR` 知识可查阅 [SignalR 官方文档](https://docs.microsoft.com/zh-cn/aspnet/signalr/) 或 [ASP.NET Core SignalR](https://docs.microsoft.com/zh-cn/aspnet/core/signalr/introduction?view=aspnetcore-5.0) 章节。

:::
